/**
 * @file bsp_speaker_driver.c
 * @author liufeng (liufeng@injoinic.com)
 * @brief wt588F02B语音模块驱动
 * @version 0.1
 * @date 2024-12-12
 *
 * @copyright Copyright (c) 2024  injoinic
 *
 */

#include "bsp_speaker_driver.h"
#include "main.h"
#include "stdio.h"
#include "tim.h"

#ifdef USE_FULL_ASSERT
#define assert_failed(char, int)              \
    do {                                      \
        printf("Error:%s,%d\r\n", char, int); \
        while (1) {                           \
        }                                     \
    } while (0)
#define assert_param(expr) \
    if (!expr)             \
    assert_failed(__FILE__, __LINE__)
#else
#define assert_param(expr) ((void)0U)
#endif

/* ------------------------------------------------------------------------------------ */
/*                                         函数声明                                      */
/* ------------------------------------------------------------------------------------ */

wt588_driver_typedef wt588_instance;                      // wt588实例对象
static void wt588_dma_callback(DMA_HandleTypeDef* _hdma); // 中断回调函数

/* ------------------------------------------------------------------------------------ */
/*                                         函数定义                                      */
/* ------------------------------------------------------------------------------------ */

/**
 * @brief wt588及底层外设初始化
 */
static void wt588_init()
{
    HAL_DMA_RegisterCallback(
        htim2.hdma[TIM_DMA_ID_UPDATE], HAL_DMA_XFER_CPLT_CB_ID, wt588_dma_callback
    );                                            // 注册DMA中断回调函数
    __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE); // 定时器Update事件触发DMA请求
}

/**
 * @brief 一线串口控制程序（单字节指令），使用定时器PWM + DMA方式
 * @note 该函数只能在任务中使用，不可以在中断函数中使用
 * @param data 待发送数据
 */
static void wt588_send_byte(uint8_t data)
{
    // 根据待发送数据，配置cmp数组
    // 数组前7个元素0，用于产生5.6ms低电平
    // 数组最后一个元素，用于等待最后一个upd事件
    static uint32_t cmp_buff[16] = {0};
    cmp_buff[15] = 800;
    for (uint8_t i = 7; i < 15; ++i) {
        if (data & 0x01)
            cmp_buff[i] = 600; // 数据1对应的pwm比较值
        else
            cmp_buff[i] = 200; // 数据0对应的pwm比较值
        data = data >> 1;
    }

    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 开启PWM输出
    HAL_DMA_Start_IT(                         // 配置并开启DMA
        htim2.hdma[TIM_DMA_ID_UPDATE],
        (uint32_t)cmp_buff,
        (uint32_t)&htim2.Instance->CCR1,
        16
    );

    // 等待发送完成
    xSemaphoreTake(wt588_instance.tx_cmp, portMAX_DELAY);
}

/**
 * @brief DMA TC中断回调函数，标志一个字节发送完成
 * @param[in] hdma dma句柄
 */
static void wt588_dma_callback(DMA_HandleTypeDef* hdma)
{
    if (hdma == htim2.hdma[TIM_DMA_ID_UPDATE]) {
        HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);            // 停止PWM输出
        xSemaphoreGiveFromISR(wt588_instance.tx_cmp, NULL); // 发送完成标志
    }
}

/**
 * @brief 播放语音（会打断当前语音，不打断需要加前缀0xF3）
 * @param[in] index 语音索引，最大数值视芯片而定
 */
static void wt588_start_play(uint8_t index)
{
    assert_param(index < 244);
    wt588_send_byte(index);
}

/**
 * @brief 停止播放（可用于打断）
 */
static void wt588_stop_play()
{
    wt588_send_byte(0xFE);
}

/**
 * @brief 调节音量
 * @param[in] volume 数值越大音量越高，应在0xE0和0xEF之间(包含0xE0和0xEF)
 */
static void wt588_set_volume(uint8_t volume)
{
    assert_param(volume >= 0xE0 && volume <= 0xEF);
    wt588_send_byte(volume);
}

// 检测busy引脚电平是否为低
static uint8_t wt588_read_busy()
{
    if (HAL_GPIO_ReadPin(SPK_BUSY_GPIO_Port, SPK_BUSY_Pin) == GPIO_PIN_RESET)
        return 1;
    return 0;
}

/* -------------------------------------------------------------------------- */
/*                                    构造函数                                 */
/* -------------------------------------------------------------------------- */

/**
 * @brief wt588F02B模块构造函数
 * @param[in] wt588_instance wt58802b实例对象
 */
void wt588_construct(wt588_driver_typedef* wt588_instance)
{
    assert_param(wt588_instance);
    wt588_instance->tx_cmp = xSemaphoreCreateBinary();
    assert_param(wt588_instance->tx_cmp);

    wt588_instance->pf_spk_busy = wt588_read_busy;
    wt588_instance->pf_init = wt588_init;
    wt588_instance->pf_start_play = wt588_start_play;
    wt588_instance->pf_stop_play = wt588_stop_play;
    wt588_instance->pf_set_volume = wt588_set_volume;
}

/* -------------------------------------------------------------------------- */
/*                                    测试函数                                 */
/* -------------------------------------------------------------------------- */

/**
 * @brief 一线串口单字节连码示例(F3 + 01 + F3 + 02)
 * @note 该函数必须在任务中被使用
 */
void wt588_list_play()
{
    wt588_send_byte(0xF3);
    vTaskDelay(pdMS_TO_TICKS(2)); // 延时2ms
    wt588_send_byte(0x01);
    vTaskDelay(pdMS_TO_TICKS(5)); // 延时5ms

    wt588_send_byte(0xF3);
    vTaskDelay(pdMS_TO_TICKS(2)); // 延时2ms
    wt588_send_byte(0x02);
    vTaskDelay(pdMS_TO_TICKS(5)); // 延时5ms
}

void test_task()
{
    // 初始化构造
    wt588_construct(&wt588_instance);
    wt588_init();

    while (1) {
        wt588_set_volume(0xE2);
        // wt588_list_play();

        wt588_start_play(0x00);
        vTaskDelay(pdMS_TO_TICKS(50));
        wt588_stop_play();
        wt588_start_play(0x01);

        vTaskDelay(pdMS_TO_TICKS(15000));
    }
}
